학교 GAS 과제

Posted by ho95

서론

지난 주엔 NASM 과제가 나왔습니다…

그리고 이번 주엔 같은 내용의 GAS 과제가 나왔습니다…

동작 내용은 같고 스타일만 NASM에서 GAS로 바꾸면 됩니다. (+다른 내용 과제 하나더…)

간단한 프로그램도 어셈블리어로 만드려니 어색하고 어려운 것 같습니다 ㅎㅎ..

항상 글의 가독성이 좋지 못합니다. 양해 부탁드립니다.

(글 잘 정리해서 쓰고 싶지만.. 바로 적지않으면 머릿속에서 사라지네요.)

GAS(GNU Assembler)

GAS는 GNU ASsembler의 약자입니다.

NASM과 기본적인 내용은 거의 같습니다만 차이가 있긴 합니다.

옛날 글에도 적었는데, 오퍼랜드의 순서와 레지스터와 상수 앞에 %,$가 붙는 것입니다.

NASM에서 mov eax, 0x41의 경우 eax=0x41이였으나 GAS는 ( mov $0x41, %eax ) 이와 같이

적어주어야 eax=0x41의 의미가 됩니다. 그리고 NASM에서는 명령어(mov,add,inc등등)에 접미어가

안 붙었지만 GAS에서는 붙혀주어야 합니다. 접미어로 b,w,l,q 등이 있습니다. 각 1,2,4,8바이트 입니다.

그리고 (%eax)와 같이 작성시 인 다이렉션이 발생합니다. %eax에 있는 주소속 값을 의미합니다.

반대로 .data나 .bss등등에 선언된 변수 앞에 $가 붙으면 변수의 값이 아닌 주소를 의미합니다.

1
2
3
4
5
6
7
.section .data			  #NASM에서는 section만 적었으나 GAS에서는 .section이라 작성
msg: .int , 4 #변수 선언은 변수명: .타입 , 값 으로 선언합니다.
msg2: .string "Hello" #NASM에서는 :이 안들어가고 타입을 db, dw등으로 선언하나
msg3: .byte 0x41 #GAS에서는 .byte, .word, .int 등으로 선언
.section .bss
.comm 변수명, 할당할 바이트 #NASM에서는 resb등등으로 크기를 나타냈으나
.comm buf, 4 #GAS는 .comm과 함께 이름, 크기로 선언(4바이트 buf 할당)

과제1: 사용자가 한자리수 입력후 그 수 만큼 빈 줄 출력후 A 출력하기

이걸 하면서 또 난관에 봉착 했습니다…

수업시 사용하던 Centos7에서는 문제 없이 됬는데, 그걸 우분투(18.04)로 가져오니 실행 하자마자

바로 세그먼트 폴트를 뿜어 주었습니다. 이것 저것 찾아보니 스택 프레임과 관련되어 발생한 것

같았습니다. 64비트 머신 기준 rbp, rsp로 스택 프레임을 관리 하는데, Centos에서 작성한 코드는

그런 rbp, rsp 조작 내용이 없었습니다. 그래서 그 조작 내용을 추가 해주니 잘 작동합니다.

나중에… NASM도 수정 해야겠네요.

​ 실행 결과입니다.

NASM은 nasm으로 컴파일을 해주고 gcc로 링크를 해주어야 했는데, GAS는 gcc하나로 다 됩니다.

HW3_2.01

​ 소스 코드입니다.

GAS는 좋은게, C의 라이브러리들을 사용할 수있습니다. 동적 링킹 덕분에 가능하다고 합니다.

NASM같은 경우 시스템 콜 번호를 eax에 넣고, ebx,ecx,edx 다 조작 하고 int 0x80으로 입출력을

했는데, GAS는 call로 불러주면 되니 비교적 간편합니다.

소스 코드에 movl, decl같이 접미사가 붙은게 보이나요? 생략하면 gcc가 알아서 붙혀 준다고

합니다만 완벽하진 않다고 합니다. printf와 scanf는 esi, edi를 조작하여 사용합니다.

esi가 입출력할 내용, edi에 입출력 형식입니다. 설명을 조금 해보자면

1
2
3
4
movl $N, %esi				# N의 주소를 esi에 넣습니다.
movl $str, %edi # str의 주소를 edi에 넣습니다
movl $0, %eax # eax는 다른 곳에서도 자주 쓰이니 0으로 초기화 해둡니다.
call scanf # scanf를 호출합니다. C언어로는 scanf("%d", &N)과 동일

이해가 좀 되실까요? 아니면 시스템 콜을 호출해서 사용해도 되나 GAS에서 그 방법은

아직 모르겠습니다.. 그후 L1 몇번 반복하다가 마지막에서 A를 출력하고 종료되는 코드입니다.

과제2: N1,N2(N1<N2)를 입력받아 N1~N2까지의 합을 출력하는 프로그램

이것도 위와 거의 비슷합니다. 단지… 할때보니 cmp 명령어에서 조금 막혔지만요 ㅎㅎ.

HW3_3.02

​ 실행 결과입니다.

1,2를 입력해서 1+2의 값인 3을 출력, 1,3을 입력하여 1+2+3인 6을 출력 했습니다.

HW3_3.01

​ 소스 코드입니다.

scanf랑 printf는 위에 설명 했으니 이해가 되시겠죠?

문제는 cmp였습니다… 처음엔 cmp in2, in1 이렇게 적었더니, 메모리 레퍼런스가 많다고…

컴파일이 안됬었는데, 고정값인 in2를 범용 레지스터(r8d)에 넣고 cmp를 해주니 작동이 되네요.

명령어의 접미사에 따른 레지스터 이름도 잘 적어주셔야 합니다.

64비트 기준, q가 오면 rax, l이오면 eax, w가오면 ax, b가오면 ah / al을 적어 주셔야 합니다.

32비트는 l까지만 사용할 수있습니다.

GAS가 까다로운 이유는 대부분의 아키텍처에서 쓸 수있는 범용이기 때문이라고 합니다.

반면 NASM은 intel 계열에서만 사용 가능하다고 하네요.

끝!